﻿using FactoryDeviceSystem.Controls;
using FactoryDeviceSystem.Models;
using FactoryDeviceSystem.Utils;
using Newtonsoft.Json;
using S7.Net;
using S7.Net.Types;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FactoryDeviceSystem
{
    public partial class MainDeviceForm : Form
    {
        bool isRunning = false;
        bool isFirst = true;
        Plc plc;
        PLCInfo plcInfo;
        System.Timers.Timer timer;
        Dictionary<int, bool> devStates = new Dictionary<int, bool>();
        Dictionary<int,DeviceData> devList = new Dictionary<int, DeviceData>();
        List<AlarmInfo> alarmList = new List<AlarmInfo>();
        List<DeviceInfo> devices;
        public MainDeviceForm()
        {
            InitializeComponent();
        }
        private void MainDeviceForm_Load(object sender, EventArgs e)
        {
            LoadDeviceAndSwitch();
            LoadPlcInfo();
            timer = new System.Timers.Timer();
            timer.Interval = 1000;
            timer.Elapsed += Timer_Elapsed;
        }

        private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Task.Run(() =>
            {
                ReadAndLoad();
            });
        }

        private int devRunCount;

        public int DevRunCount
        {
            get 
            { 
                return devRunCount;
            }
            set 
            { 
                devRunCount = value;
                runCountLb.Text = devRunCount + "台";
            }
        }

        private int devStopCount;

        public int DevStopCount
        {
            get
            {
                return devStopCount;
            }
            set
            {
                devStopCount = value;
                stopCountLb.Text = devStopCount + "台";
            }
        }

        private void LoadPlcInfo()
        {
            if (plcInfo == null)
            {
                plcInfo = ConfigUtil.LoadPlcConfig();
            }
            
            if(plcInfo == null)
            {
                MessageBox.Show("当前暂未配置PLC链接信息");
                return;
            }

            plc = new Plc(plcInfo.CPUType, plcInfo.IP, plcInfo.Port, plcInfo.Rack, plcInfo.Slot);
        }

        private void ReadAndLoad()
        {
            ReadData();
            LoadData();
        }

        private void ReadData()
        {
            int count = devices.Count;
            var states = (BitArray)plc.Read(DataType.Input,0,0,VarType.Bit,count);

            //读设备数据 功率/速度/温度/压力
            ushort[] powers = new ushort[count];
            ushort[] speeds = new ushort[count];
            float[] temps = new float[count];
            float[] pressures = new float[count];

            foreach (var store in ConfigUtil.LoadStores())
            {
                VarType vType = (VarType)Enum.Parse(typeof(VarType), store.ReadType);
                if (store.ParaType == "Power")
                {
                    powers = (ushort[])plc.Read(DataType.DataBlock, 1, store.FirstPosition, vType, count);
                }
                else if (store.ParaType == "Speed")
                {
                    speeds = (ushort[])plc.Read(DataType.DataBlock, 1, store.FirstPosition, vType, count);
                }
                else if (store.ParaType == "Temperature")
                {
                    uint[] val = (uint[])plc.Read(DataType.DataBlock, 1, store.FirstPosition, vType, count);
                    temps = ConvertToSingle(val);
                }
                else if (store.ParaType == "Pressure")
                {
                    uint[] val = (uint[])plc.Read(DataType.DataBlock,1,store.FirstPosition,vType, count);
                    pressures = ConvertToSingle(val);
                }
            }

            foreach (var device in devices)
            {
                int pos = device.Position;
                int devNo = device.DeviceNo;
                if (devStates.ContainsKey(devNo))
                {
                    devStates[devNo] = states[pos];
                }
                else
                {
                    devStates.Add(devNo, states[pos]);
                }

                DeviceData deviceData = new DeviceData();
                deviceData.Power = powers[pos];
                deviceData.Speed = speeds[pos];
                deviceData.Temperature = temps[pos];
                deviceData.Pressure = pressures[pos];

                if (devList.ContainsKey(devNo))
                {
                    devList[devNo] = deviceData;
                }
                else
                {
                    devList.Add(devNo, deviceData);
                }
            }
        }
        
        private float[] ConvertToSingle(uint[] vals)
        {
            List<float> list = new List<float>();
            foreach (var val in vals)
            {
                byte[] buff = BitConverter.GetBytes(val);
                float v = BitConverter.ToSingle(buff, 0);
                list.Add(v);
            }

            return list.ToArray();
        }

        private void LoadData()
        {
            this.Invoke(new Action(() =>
            {
                DevRunCount = devStates.Where(d => d.Value).Count();
                DevStopCount = devStates.Count() - DevRunCount;

                foreach (var control in flPanel1.Controls)
                {
                    DeviceControl devControl = (control as DeviceControl);
                    int devNo = int.Parse(devControl.DevNo);
                    DeviceData devData = devList[devNo];
                    bool state = devStates[devNo];
                    if (state)
                    {
                        devControl.State = true;
                        devControl.Power = devData.Power;
                        devControl.Speed = devData.Speed;
                        devControl.Temperature = devData.Temperature;
                        devControl.Pressure = devData.Pressure;

                        if (!isFirst)
                            CheckDeviceData(devNo,devControl.DevName,devData);
                    }
                    else
                    {
                        devControl.State = false;
                        devControl.Power = 0;
                        devControl.Speed = 0;
                        devControl.Temperature = 0;
                        devControl.Pressure = 0;
                    }
                }

                if (isFirst)
                {
                    InitCheckData();
                    isFirst = false;
                }

                foreach (var control in flPanel2.Controls)
                {
                    UDevice uDevice = (control as UDevice);
                    int devNo = uDevice.DeviceNo;
                    bool state = devStates[devNo];
                    uDevice.State = state;
                    if (state)
                    {
                        uDevice.CanOpen = false;
                        uDevice.CanClose = true;
                    }
                    else
                    {
                        uDevice.CanOpen = true;
                        uDevice.CanClose = false;
                    }
                }
            }));
        }

        Dictionary<int,DeviceData> lastDeviceDataList = new Dictionary<int,DeviceData>();
        private void InitCheckData()
        {
            foreach (int devNo in devList.Keys)
            {
                if (devStates[devNo])
                    continue;
                DeviceData deviceData = devList[devNo];
                if (!lastDeviceDataList.ContainsKey(devNo))
                {
                    lastDeviceDataList.Add(devNo,deviceData);
                }

                string devName = devices.Find(d => d.DeviceNo == devNo).DeviceName;
                if(deviceData.Temperature > 30)
                {
                    AlarmInfo alarmInfo = new AlarmInfo(devNo, devName, "温度过高", deviceData.Temperature.ToString("##.#"));
                    AddAlarmInfo(alarmInfo);
                }
                if(deviceData.Pressure < 3)
                {
                    AlarmInfo alarmInfo = new AlarmInfo(devNo, devName, "压力过低", deviceData.Pressure.ToString("##.#"));
                    AddAlarmInfo(alarmInfo);
                }
                else if(deviceData.Pressure > 16)
                {
                    AlarmInfo alarmInfo = new AlarmInfo(devNo, devName, "压力过高", deviceData.Pressure.ToString("##.#"));
                    AddAlarmInfo(alarmInfo);
                }

                if(deviceData.Speed < 300)
                {
                    AlarmInfo alarmInfo = new AlarmInfo(devNo, devName, "速度过低", deviceData.Speed.ToString());
                    AddAlarmInfo(alarmInfo);
                }
                else if(deviceData.Speed > 1000)
                {
                    AlarmInfo alarmInfo = new AlarmInfo(devNo, devName, "速度过高", deviceData.Speed.ToString());
                    AddAlarmInfo(alarmInfo);
                }
            }
        }

        private void CheckDeviceData(int devNo,string devName,DeviceData devData)
        {
            if (!lastDeviceDataList.ContainsKey(devNo)) 
            {
                lastDeviceDataList.Add(devNo, devData);
            }

            DeviceData lastDevData = lastDeviceDataList[devNo];
            // 之前温度正常，当前温度不正常
            if (lastDevData.Temperature > 30 && devData.Temperature < 30)
            {
                AlarmInfo alarmInfo = new AlarmInfo(devNo, devName, "温度过低", devData.Temperature.ToString("##.#"));
                AddAlarmInfo(alarmInfo);
            }
            // 之前温度不正常，当前温度正常
            else if (lastDevData.Temperature < 30 && devData.Temperature > 30)
            {
                AlarmInfo alarmInfo = new AlarmInfo(devNo, devName,"取消报警", "温度正常", devData.Temperature.ToString("##.#"));
                AddAlarmInfo(alarmInfo);
            }

            // 之前的压力过低，当前的压力正常
            if (lastDevData.Pressure < 3 &&(devData.Pressure > 3 && devData.Pressure < 16))
            {
                AlarmInfo alarmInfo = new AlarmInfo(devNo, devName, "取消报警", "压力正常", devData.Pressure.ToString("##.#"));
                AddAlarmInfo(alarmInfo);
            }
            // 之前的压力正常，当前的压力过低
            else if(devData.Pressure < 3 &&(lastDevData.Pressure > 3 && lastDevData.Pressure < 16))
            {
                AlarmInfo alarmInfo = new AlarmInfo(devNo, devName, "压力过低", devData.Pressure.ToString("##.#"));
                AddAlarmInfo(alarmInfo);
            }

            // 之前压力过高，当前压力正常
            if(lastDevData.Pressure > 16 && (devData.Pressure > 3 && devData.Pressure < 16))
            {
                AlarmInfo alarmInfo = new AlarmInfo(devNo, devName, "取消报警", "压力正常", devData.Pressure.ToString("##.#"));
                AddAlarmInfo(alarmInfo);
            }
            // 之气那的压力正常，当前的压力过高
            else if (devData.Pressure > 16 && (lastDevData.Pressure > 3 && lastDevData.Pressure < 16))
            {
                AlarmInfo alarmInfo = new AlarmInfo(devNo, devName, "压力过高", devData.Pressure.ToString("##.#"));
                AddAlarmInfo(alarmInfo);
            }

            // 之前的速度过低，当前正常
            if(lastDevData.Speed < 300 &&(devData.Speed > 300 && devData.Speed < 1000))
            {
                AlarmInfo alarmInfo = new AlarmInfo(devNo, devName, "取消报警", "速度正常", devData.Speed.ToString());
                AddAlarmInfo(alarmInfo);
            }
            // 之前的速度正常，当前过低
            else if(devData.Speed < 300 && (lastDevData.Speed > 300 && lastDevData.Speed < 1000))
            {
                AlarmInfo alarmInfo = new AlarmInfo(devNo, devName, "速度过低", devData.Speed.ToString());
                AddAlarmInfo(alarmInfo);
            }

            // 之前的速度过高，当前正常
            if (lastDevData.Speed > 1000 && (devData.Speed > 300 && devData.Speed < 1000))
            {
                AlarmInfo alarmInfo = new AlarmInfo(devNo, devName, "取消报警", "速度正常", devData.Speed.ToString());
                AddAlarmInfo(alarmInfo);
            }
            // 之前的速度正常，当前过高
            else if (devData.Speed > 1000 && (lastDevData.Speed > 300 && lastDevData.Speed < 1000))
            {
                AlarmInfo alarmInfo = new AlarmInfo(devNo, devName, "速度过高", devData.Speed.ToString());
                AddAlarmInfo(alarmInfo);
            }

            lastDeviceDataList[devNo] = devData;
        }

        //添加报警记录
        private void AddAlarmInfo(AlarmInfo alarm)
        {
            if(alarmList.Count > 20)
            {
                alarmList.RemoveAt(0);
            }
            alarmList.Add(alarm);
            ReloadDgView();
        }

        Point startPoint;
        bool isMouseDown = false;
        private void panel1_MouseDown(object sender, MouseEventArgs e)
        {
            startPoint = e.Location;
            isMouseDown = true;
        }

        private void panel1_MouseMove(object sender, MouseEventArgs e)
        {
            if (isMouseDown)
            {
                Point destP = e.Location;
                this.Location = new Point(this.Location.X + destP.X - startPoint.X, this.Location.Y + destP.Y - startPoint.Y);
            }
        }

        private void panel1_MouseUp(object sender, MouseEventArgs e)
        {
            isMouseDown = false;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            this.WindowState = FormWindowState.Minimized;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void MainDeviceForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            DialogResult dialog = MessageBox.Show("确认关闭页面？","提示",MessageBoxButtons.YesNo,MessageBoxIcon.Question);
            if(dialog == DialogResult.Yes)
            {
                Application.ExitThread();
            }
            else
            {
                e.Cancel = true;
            }
        }

        // 开始|停止监控
        private void startBtn_Click(object sender, EventArgs e)
        {
            LoadPlcInfo();
            if (plc == null)
            {
                MessageBox.Show("PLC链接失败");
                return;
            }
           
            UButton btn = (sender as UButton);
            try
            {
                if (!isRunning)
                {
                    plc.Open();
                    if (!plc.IsConnected)
                    {
                        throw new Exception("链接打开失败");
                    }
                    else
                    {
                        btn.Text = "停止监控";
                        runStateLb.ForeColor = Color.LimeGreen;
                        runStateLb.Text = "正在监控...";
                        InitControlState(true);
                        timer.Start();
                    }
                }
                else
                {
                    plc.Close();
                    if (plc.IsConnected)
                    {
                        throw new Exception("链接关闭失败");
                    }
                    else
                    {
                        btn.Text = "启动设备";
                        runStateLb.ForeColor = Color.FromArgb(0, 120, 215);
                        runStateLb.Text = "未连接";
                        InitControlState(false);
                        timer.Stop();
                    }
                }
                isRunning = !isRunning;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void InitControlState(bool flag)
        {
            allOpenBtn.Enabled = flag;
            allCloseBtn.Enabled = flag;
            foreach (UDevice control in flPanel2.Controls)
            {
                control.CanOpen = flag;
                control.CanClose = flag;
            }
        }

        private void ResetPlc()
        {
            if (isRunning)
            {
                if(plc!=null && plc.IsConnected)
                {
                    plc.Close();
                }
                timer.Stop();
                isRunning = false;
            }
        }

        private void comuSetBtn_Click(object sender, EventArgs e)
        {
            ResetPlc();
            PLCSettingForm form = new PLCSettingForm();
            form.OnConfirm += Plc_SetConfirm;
            form.ShowDialog();
        }

        private void Plc_SetConfirm(object sender, DataEventArgs e)
        {
            plcInfo = e.Data as PLCInfo;
        }

        private void devSetBtn_Click(object sender, EventArgs e)
        {
            ResetPlc();
            DeviceSettingForm form = new DeviceSettingForm();
            form.ShowDialog();
        }

        private void storeSetBtn_Click(object sender, EventArgs e)
        {
            ResetPlc();
            StoreSettingForm form = new StoreSettingForm();
            form.ShowDialog();
        }

        private void allOpenBtn_Click(object sender, EventArgs e)
        {
            DataItem[] dataItems = CreateDateItems(true);
            plc.Write(dataItems);
        }

        private void allCloseBtn_Click(object sender, EventArgs e)
        {
            DataItem[] dataItems = CreateDateItems(false);
            plc.Write(dataItems);
        }

        private DataItem[] CreateDateItems(bool state)
        {
            List<DataItem> dataItems = new List<DataItem>();
            foreach (var device in devices)
            {
                DataItem dataItem = new DataItem();
                dataItem.DataType = DataType.Input;
                dataItem.VarType = VarType.Bit;
                dataItem.DB = 0;
                string stateAddr = device.StateAddress;
                dataItem.StartByteAdr = int.Parse(stateAddr.Split('.')[0]);
                dataItem.BitAdr = byte.Parse(stateAddr.Split('.')[1]);
                dataItem.Value = state;
                dataItems.Add(dataItem);
            }

            return dataItems.ToArray();
        }

        // 加载设备列表和开关控制列表
        private void LoadDeviceAndSwitch()
        {
            devices = ConfigUtil.LoadDevices();

            foreach (var device in devices)
            {
                DeviceControl control = new DeviceControl();
                control.DevNo = device.DeviceNo.ToString();
                control.DevName = device.DeviceName;
                control.State = false;
                flPanel1.Controls.Add(control);

                UDevice uDevice = new UDevice();
                uDevice.DeviceNo = device.DeviceNo;
                uDevice.DeviceName = device.DeviceName;
                uDevice.State = false;
                uDevice.CanOpen = false;
                uDevice.CanClose = false;
                uDevice.StartEvent += UDevice_StartEvent;
                uDevice.StopEvent += UDevice_StopEvent;
                flPanel2.Controls.Add(uDevice);
            }
            allOpenBtn.Enabled = false;
            allCloseBtn.Enabled = false;
        }

        private void UDevice_StartEvent(object sender, EventArgs e)
        {
            UDevice device = sender as UDevice;
            ChangeDeviceState(device, true);
        }

        private void UDevice_StopEvent(object sender, EventArgs e)
        {
            UDevice device = sender as UDevice;
            ChangeDeviceState(device, false);
        }

        private void ChangeDeviceState(UDevice device, bool state)
        {
            DeviceInfo deviceInfo = devices.Find(d => d.DeviceNo == device.DeviceNo);
            string addr = "I"+deviceInfo.StateAddress;
            plc.Write(addr, state);
            if (state)
            {
                device.CanClose = true;
                device.CanOpen = false;
            }
            else
            {
                device.CanClose = false;
                device.CanOpen = true;
            }
        }


        private void dataGridView1_DataSourceChanged(object sender, EventArgs e)
        {
            for (int i = 0; i < dgView.Rows.Count; i++)
            {
                AlarmInfo alarmInfo = dgView.Rows[i].DataBoundItem as AlarmInfo;
                if(alarmInfo.AlarmState == "报警")
                {
                    dgView.Rows[i].Cells["AlarmState"].Style.ForeColor = Color.Red;
                }
                else
                {
                    dgView.Rows[i].Cells["AlarmState"].Style.ForeColor = Color.Green;
                }
            }
        }

        private void ReloadDgView()
        {
            if(dgView.AutoGenerateColumns == true)
                dgView.AutoGenerateColumns = false;
            dgView.DataSource = null;
            dgView.DataSource = alarmList;
        }
    }
}
